#include "..\wow6mapper\wow6mapper\ChunkHeader.h"
#include "obj0.h"
#include "log.h"

extern Log * pLog;

Obj0::Obj0(const BufferS_t &obj_buf) {
	// create an istream of our buffer
	std::stringbuf str_buf(obj_buf);
	std::istream i_str(&str_buf);

	std::streamoff chunk_start = i_str.tellg();
	std::streamoff cur_pos = chunk_start;
	ChunkHeader_s header;
	uint32_t size = obj_buf.size();

	// the same amount of MCNKs as in the usual *.adt files
	_objectRefs.resize(256);
	size_t zero_mem_size = sizeof(ObjMcnkChunk_s)+sizeof(McrdChunk_s)+
		sizeof(McrwChunk_s);

	int mcnk_count = 0;

	while (cur_pos < size)
	{
		i_str.read((char*)&header, sizeof(ChunkHeader_s));
		i_str.seekg(cur_pos, std::ios::beg);							// rewind the stream

		if (checkChunkId((char *)&header, "MVER"))
		{
			i_str.read((char*)&_mverChunk, sizeof(MverChunk_s));
		}
		else if (checkChunkId((char *)&header, "MMDX"))
		{
			i_str.read((char*)&_mmdxChunk, sizeof(ChunkHeader_s));

			if (_mmdxChunk.size) {
				_doodadNames.resize(_mmdxChunk.size);
				i_str.read((char*)&_doodadNames[0], _mmdxChunk.size);
			}
		}
		else if (checkChunkId((char *)&header, "MMID"))
		{
			i_str.read((char*)&_mmidChunk, sizeof(ChunkHeader_s));

			if (_mmidChunk.size) {
				_mmdxIndices.resize(_mmidChunk.size / 4);
				i_str.read((char*)&_mmdxIndices[0], _mmidChunk.size);
			}
		}
		else if (checkChunkId((char *)&header, "MWMO"))
		{
			i_str.read((char*)&_mwmoChunk, sizeof(ChunkHeader_s));

			if (_mwmoChunk.size) {
				_wmoNames.resize(_mwmoChunk.size);
				i_str.read((char*)&_wmoNames[0], _mwmoChunk.size);
			}
		}
		else if (checkChunkId((char *)&header, "MWID"))
		{
			i_str.read((char*)&_mwidChunk, sizeof(ChunkHeader_s));

			if (_mwidChunk.size) {
				_mwmoIndices.resize(_mwidChunk.size / 4);
				i_str.read((char*)&_mwmoIndices[0], _mwidChunk.size);
			}
		}
		else if (checkChunkId((char *)&header, "MDDF"))
		{
			i_str.read((char*)&_mddfChunk, sizeof(ChunkHeader_s));

			if (_mddfChunk.size) {
				_doodadInfo.resize(_mddfChunk.size / sizeof(MddfChunk_s::DoodadInfo_s));
				i_str.read((char*)&_doodadInfo[0], _mddfChunk.size);
			}
		}
		else if (checkChunkId((char *)&header, "MODF"))
		{
			i_str.read((char*)&_modfChunk, sizeof(ChunkHeader_s));

			if (_modfChunk.size) {
				_wmoInfo.resize(_modfChunk.size / sizeof(ModfChunk_s::WmoInfo_s));
				i_str.read((char*)&_wmoInfo[0], _modfChunk.size);
			}
		}
		else if (checkChunkId((char *)&header, "MCNK"))
		{
			memset(&_objectRefs[mcnk_count], 0, zero_mem_size);					// set all chunks to zero

			// there is always a MCNK chunk first

			ObjMcnkChunk_s &mcnk_chunk = _objectRefs[mcnk_count].mcnkChunk;		//looks like this should be indexed not always the 0 chunk
			i_str.read((char*)&mcnk_chunk, sizeof(ObjMcnkChunk_s));

			std::streamoff data_off = i_str.tellg();

			if (mcnk_chunk.size)
			{
				cur_pos = i_str.tellg();
				i_str.read((char*)&header, sizeof(ChunkHeader_s));
				i_str.seekg(cur_pos, std::ios::beg);

				if (checkChunkId((char *)&header, "MCRD"))
				{
					//Check if next is MCRD or MCRW //NONO
					McrdChunk_s &mcrd_chunk = _objectRefs[mcnk_count].mcrdChunk;
					i_str.read((char*)&mcrd_chunk, sizeof(McrdChunk_s));

					// read doodad indices
					_objectRefs[mcnk_count].doodadIndices.resize(mcrd_chunk.size / 4);
					i_str.read((char*)&_objectRefs[mcnk_count].doodadIndices[0], mcrd_chunk.size);
				}

				//check if there's a MCRW chunk left to read
				if ((data_off + mcnk_chunk.size) > i_str.tellg())
				{
					cur_pos = i_str.tellg();
					i_str.read((char*)&header, sizeof(ChunkHeader_s));
					i_str.seekg(cur_pos, std::ios::beg);

					if (checkChunkId((char *)&header, "MCRW"))
					{
						McrwChunk_s &mcrw_chunk = _objectRefs[mcnk_count].mcrwChunk;
						i_str.read((char*)&mcrw_chunk, sizeof(McrwChunk_s));

						// read WMO indices
						_objectRefs[mcnk_count].wmoIndices.resize(mcrw_chunk.size / 4);
						i_str.read((char*)&_objectRefs[mcnk_count].wmoIndices[0], mcrw_chunk.size);
					}
					else
					{
						bool what_is_this = true;
					}
				}
			}
			mcnk_count++;
		}
		else
		{
			// should never get here
			pLog->Write("obj0 unknow chunk %s", header.id);
			i_str.seekg(cur_pos + 0x8 + header.size, std::ios::beg);
		}
		cur_pos = i_str.tellg();
	}

	if (mcnk_count != 256)
		bool bad = true;
}

//------------------------------------------------------------------------------
void Obj0::parseObjectReferences(std::istream &i_str) {
	// the same amount of MCNKs as in the usual *.adt files
	_objectRefs.resize(256);
	size_t zero_mem_size = sizeof(ObjMcnkChunk_s)+sizeof(McrdChunk_s)+
		sizeof(McrwChunk_s);

	// get all object reference MCNKs
	for (int i = 0; i < 256; i++) {
		memset(&_objectRefs[i], 0, zero_mem_size); // set all chunks to zero

		// there is always a MCNK chunk first
		ObjMcnkChunk_s &mcnk_chunk = _objectRefs[0].mcnkChunk;
		i_str.read((char*)&mcnk_chunk, sizeof(ObjMcnkChunk_s));
		std::streamoff data_off = i_str.tellg();

		if (mcnk_chunk.size) {

			if (i_str.peek() == 'D') { //Check if next is MCRD or MCRW //NONO
				McrdChunk_s &mcrd_chunk = _objectRefs[i].mcrdChunk;
				i_str.read((char*)&mcrd_chunk, sizeof(McrdChunk_s));

				// read doodad indices
				_objectRefs[i].doodadIndices.resize(mcrd_chunk.size / 4);
				i_str.read((char*)&_objectRefs[i].doodadIndices[0], mcrd_chunk.size);
			}

			// check if there's a MCRW chunk left to read
			if ((data_off + mcnk_chunk.size) > i_str.tellg()) {
				McrwChunk_s &mcrw_chunk = _objectRefs[i].mcrwChunk;
				i_str.read((char*)&mcrw_chunk, sizeof(McrwChunk_s));

				// read WMO indices
				_objectRefs[i].wmoIndices.resize(mcrw_chunk.size / 4);
				i_str.read((char*)&_objectRefs[i].wmoIndices[0], mcrw_chunk.size);
			}
		}
	}
}

//------------------------------------------------------------------------------
const ObjectReferences_t& Obj0::getObjectRefs() const {
	return _objectRefs;
}

//------------------------------------------------------------------------------
const WmoInformations_t& Obj0::wmoInfo() const {
	return _wmoInfo;
}

//------------------------------------------------------------------------------
void Obj0::getDoodad(uint32_t index, Doodad_s *doodad) const {
	if (index + 1 > _doodadInfo.size()) return;

	const MddfChunk_s::DoodadInfo_s &doodad_info = _doodadInfo[index];
	uint32_t mmid = doodad_info.id;  // index into the doodad indices
	uint32_t name_off = _mmdxIndices[mmid]; // name offset 

	std::stringbuf str_buf(_doodadNames);
	std::istream i_str(&str_buf);

	char dn_buf[256];                   // buffer to hold the name returned by getline
	i_str.seekg(name_off);            // position stream at the right position
	i_str.getline(dn_buf, 256, '\0');
	std::string doodad_name(dn_buf);  // I like strings :)

	// return data
	memcpy(&doodad->info, &doodad_info, sizeof(MddfChunk_s::DoodadInfo_s));
	doodad->name = doodad_name;
}

//------------------------------------------------------------------------------
void Obj0::getWmo(uint32_t index, Wmo_s *wmo) const {
	if (index + 1 > _wmoInfo.size()) return;

	// pretty much the same as in getDoodad()
	const ModfChunk_s::WmoInfo_s &wmo_info = _wmoInfo[index];
	uint32_t mwid = wmo_info.id;
	uint32_t name_off = _mwmoIndices[mwid];

	std::stringbuf str_buf(_wmoNames);
	std::istream i_str(&str_buf);

	char dn_buf[256];
	i_str.seekg(name_off);
	i_str.getline(dn_buf, 256, '\0');
	std::string wmo_name(dn_buf);

	// return data
	memcpy(&wmo->info, &wmo_info, sizeof(ModfChunk_s::WmoInfo_s));
	wmo->name = wmo_name;
}
